﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Xml.Linq;
using Mindfor.Web.Models;
using Mindfor.Web.Data;

namespace Mindfor.Web
{
	/// <summary>
	/// Provides extension functions for <see cref="HtmlHelper"/>.
	/// </summary>
	public static class HtmlHelperExtensions
	{
		#region Module

		/// <summary>
		/// Invokes the specified child action method using the specified parameters and controller name and returns the result as an HTML string.
		/// </summary>
		/// <param name="actionName">The name of the action method.</param>
		/// <param name="controllerName">The name of the controller.</param>
		/// <param name="moduleName">The name of the module.</param>
		/// <returns>The child action result as an HTML string.</returns>
		public static MvcHtmlString ActionModule(this HtmlHelper html, string actionName, string controllerName, string moduleName)
		{
			return html.Action(actionName, controllerName, new { module = moduleName });
		}

		/// <summary>
		/// Invokes the specified child action method using the specified parameters and controller name and returns the result as an HTML string.
		/// </summary>
		/// <param name="actionName">The name of the action method.</param>
		/// <param name="controllerName">The name of the controller.</param>
		/// <param name="moduleName">The name of the module.</param>
		/// <param name="routeValues">
		/// An object that contains the parameters for a route. The parameters are retrieved
		/// through reflection by examining the properties of the object. The object
		/// is typically created by using object initializer syntax.
		/// </param>
		/// <returns>The child action result as an HTML string.</returns>
		public static MvcHtmlString ActionModule(this HtmlHelper html, string actionName, string controllerName, string moduleName, object routeValues)
		{
			if (routeValues == null)
				return ActionModule(html, actionName, controllerName, moduleName);
			RouteValueDictionary values = new RouteValueDictionary(routeValues);
			values["module"] = moduleName;
			return html.Action(actionName, controllerName, values);
		}

		#endregion

		#region DisplayName

		/// <summary>
		/// Returns display name attribute value for model property.
		/// </summary>
		/// <typeparam name="TModel">Model type.</typeparam>
		/// <typeparam name="TValue">Property type.</typeparam>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">An expression that identifies the property to display.</param>
		public static string DisplayNameFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
		{
			ModelMetadata meta = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
			return meta.DisplayName ?? meta.PropertyName;
		}

		/// <summary>
		/// Returns display name attribute value for model property.
		/// </summary>
		/// <typeparam name="TModel">Model type.</typeparam>
		/// <typeparam name="TValue">Property type.</typeparam>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="expression">A string that identifies the property to display.</param>
		public static string DisplayNameFor(this HtmlHelper html, string expression)
		{
			ModelMetadata meta = ModelMetadata.FromStringExpression(expression, html.ViewData);
			return meta.DisplayName ?? meta.PropertyName;
		}

		#endregion

		#region Title

		public const string DefaultTitleDelimiter = " / ";
		
		/// <summary>
		/// Renders page title into html string for head=>title.
		/// </summary>
		/// <param name="html">HtmlHelper.</param>
		/// <param name="title">PageTitle to render.</param>
		/// <param name="inverse"><c>True</c> to inverse elements. Otherwise <c>False</c>.</param>
		/// <param name="delimiter">Delimeter between page title elements.</param>
		/// <returns>String.</returns>
		public static MvcHtmlString Head(this HtmlHelper html, PageTitleCollection title, bool inverse = true, string delimiter = DefaultTitleDelimiter)
		{
			StringBuilder sb = new StringBuilder();
			if (title != null)
			{
				foreach (PageTitleNode node in title.GetRenderItems())
				{
					string content = node.HeadText != null ? node.HeadText : node.Text;
					if (content != null)
						content = content.Trim();

					if (!String.IsNullOrEmpty(content))
					{
						if (sb.Length > 0)
						{
							if (inverse)
								sb.Insert(0, delimiter);
							else
								sb.Append(delimiter);
						}
						
						if (inverse)
							sb.Insert(0, content);
						else
							sb.Append(content);
					}
				}
			}

			return MvcHtmlString.Create(sb.ToString());
		}

		/// <summary>
		/// Renders page title into html string.
		/// </summary>
		/// <param name="html">HtmlHelper.</param>
		/// <param name="title">PageTitle to render.</param>
		/// <param name="inverse"><c>True</c> to inverse elements. Otherwise <c>False</c>.</param>
		/// <param name="delimiter">Delimeter between page title elements.</param>
		/// <param name="onlyText">True, to render text even if any node have url. Otherwise, false.</param>
		/// <returns>String.</returns>
		public static MvcHtmlString Title(this HtmlHelper html, PageTitleCollection title, bool inverse = false, bool onlyText = false, string delimiter = DefaultTitleDelimiter)
		{
			StringBuilder sb = new StringBuilder();
			if (title != null)
			{
				foreach (PageTitleNode node in title.GetRenderItems())
				{
					if (sb.Length > 0)
					{
						if (inverse)
							sb.Insert(0, delimiter);
						else
							sb.Append(delimiter);
					}

					string nodeHtml = TitleNode(html, node, onlyText).ToString();
					if (inverse)
						sb.Insert(0, nodeHtml);
					else
						sb.Append(nodeHtml);
				}
			}

			return MvcHtmlString.Create(sb.ToString());
		}

		/// <summary>
		/// Renders page title node into html string.
		/// </summary>
		/// <param name="html">HtmlHelper.</param>
		/// <param name="node">PageTitleNode to render.</param>
		/// <param name="onlyText">True to render text even if any node have url. Otherwise, false.</param>
		/// <returns>String.</returns>
		public static MvcHtmlString TitleNode(this HtmlHelper html, PageTitleNode node, bool onlyText = false)
		{
			string content = node.Text.Trim();
			if (!String.IsNullOrEmpty(node.Url) && !onlyText)
			{
				TagBuilder a = new TagBuilder("a");
				a.Attributes["href"] = node.Url;
				a.InnerHtml = content.Trim();
				return MvcHtmlString.Create(a.ToString());
			}

			return MvcHtmlString.Create(content);
		}

		#endregion

		#region Widgets

		/// <summary>
		/// Renders widgets for specific place.
		/// </summary>
		/// <param name="html">HtmlHelper.</param>
		/// <param name="place">Place of widgets to render.</param>
		public static MvcHtmlString Widgets(this HtmlHelper html, string place)
		{
			IEnumerable<WidgetModel> widgets = html.ViewData["Global.Widgets"] as IEnumerable<WidgetModel>;
			if (widgets != null)
			{
				StringBuilder sb = new StringBuilder();
				foreach (WidgetModel w in widgets.Where(r => r.Place == place))
				{
					MvcHtmlString widgetHtml = html.ActionModule(w.ActionPath.ActionName, w.ActionPath.ControllerName,
						w.ActionPath.ModuleName, new { param = w.WidgetParam });
					sb.Append(widgetHtml.ToHtmlString());
				}
				return new MvcHtmlString(sb.ToString());
			}
			return null;
		}

		#endregion

		#region Pager

		/// <summary>
		/// Renders pager. PagerData is taken from ViewData["Pager"].
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="showNavigation">True, to show next and previous links. Otherwise, false.</param>
		/// <returns>String.</returns>
		public static MvcHtmlString Pager(this HtmlHelper helper, bool showNavigation = true)
		{
			PagerModel data = helper.ViewData["Pager"] as PagerModel;
			if (data == null)
				throw new Exception("Can not get PagerData from ViewData[\"Pager\"].");
			return Pager(helper, data, showNavigation);
		}

		/// <summary>
		/// Renders pager.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="data">Pager data.</param>
		/// <param name="showNavigation">True, to show next and previous links. Otherwise, false.</param>
		/// <returns>String.</returns>
		public static MvcHtmlString Pager(this HtmlHelper helper, PagerModel data, bool showNavigation = true)
		{
			// check
			if (data == null)
				return MvcHtmlString.Empty;
			if (data.ItemsPerPage <= 0)
				throw new ArgumentException("ItemsPerPage must be more than 0.");
			const int sidePages = 2;
			const int sidePages10 = 3;
			const string prevText = "← назад";
			const string nextText = "вперед →";

			// get pages count
			int pages = (int)Math.Ceiling((double)data.ItemsCount / data.ItemsPerPage);
			if (pages <= 1)
				return MvcHtmlString.Empty;

			// get route values
			UrlHelper urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
			HttpRequestBase request = helper.ViewContext.HttpContext.Request;
			RouteValueDictionary route = helper.ViewContext.RouteData.Values;
			foreach (var key in request.QueryString.AllKeys)
			{
				route[key] = request.QueryString[key];
			}

			// get current page
			int firstPage = data.FirstPageIndex;
			int lastPage = data.FirstPageIndex + pages - 1;
			int currentPage = firstPage;
			if (data.CurrentPage.HasValue)
				currentPage = data.CurrentPage.Value;
			else if (route[data.PageKey] != null)
				int.TryParse(route[data.PageKey].ToString(), out currentPage);

			if (currentPage > lastPage)
				currentPage = firstPage;

			int diff1 = firstPage - 1;
			int currentPage1 = currentPage - diff1;
			int lastPage1 = lastPage - diff1;

			// route data
			RouteValueDictionary newRoute = new RouteValueDictionary(route);
			string actionText = string.IsNullOrEmpty(data.ActionName) ? (string)newRoute["action"] : data.ActionName;
			string controllerText = string.IsNullOrEmpty(data.ControllerName) ? (string)newRoute["controller"] : data.ControllerName;

			// --- generate html ---
			XElement container = new XElement("div", new XAttribute("class", "pager"));

			// render items

			// previous button
			if (showNavigation)
			{
				if (currentPage1 <= 1)
				{
					container.Add(new XElement("span",
						new XAttribute("class", "prev"),
						prevText)
					);
				}
				else
				{
					newRoute[data.PageKey] = currentPage1 + diff1 - 1;
					container.Add(new XElement("a",
						new XAttribute("href", urlHelper.Action(actionText, controllerText, newRoute)),
						new XAttribute("class", "prev"),
						prevText
						)
					);
				}
				// next button
				if (currentPage1 >= lastPage1)
				{
					container.Add(new XElement("span",
						new XAttribute("class", "next"),
						nextText)
					);
				}
				else
				{
					newRoute[data.PageKey] = currentPage1 + diff1 + 1;
					container.Add(new XElement("a",
						new XAttribute("href", urlHelper.Action(actionText, controllerText, newRoute)),
						new XAttribute("class", "next"),
						nextText
						)
					);
				}
			}
			
			XElement ul = new XElement("ul");
			container.Add(ul);

			// first element
			if (currentPage1 == 1)
			{
				ul.Add(CreatePagerLinkElement("1", null, true));
			}
			else
			{
				newRoute[data.PageKey] = firstPage;
				ul.Add(CreatePagerLinkElement("1", urlHelper.Action(actionText, controllerText, newRoute)));
			}

			//if (currentPage - sidePages == firstPage + 2)
			//{
			//    newRoute[data.PageKey] = firstPage + 1;
			//    ul.Add(CreatePagerLinkElement("2", urlHelper.Action(actionText, controllerText, newRoute)));
			//}

			// left side 10 pages
			int left10 = currentPage1 - sidePages - 1;
			for (; left10 > 10; left10--)
			{
				if (left10 % 10 == 0)
					break;
			}

			for (int i = sidePages10-1; i >= 0; i--)
			{
				int page = left10 - 10 * i;
				if (page < 10)
					continue;
				newRoute[data.PageKey] = page + diff1;
				ul.Add(CreatePagerLinkElement(page.ToString(), urlHelper.Action(actionText, controllerText, newRoute)));
			}

			// middle pages
			int middleFrom = Math.Max(currentPage1 - sidePages, 2);
			int middleTo = Math.Min(currentPage1 + sidePages, lastPage1-1);
			for (int i = middleFrom; i <= middleTo; i++)
			{
				if (i == currentPage1)
					ul.Add(CreatePagerLinkElement(i.ToString(), null, true));
				else
				{
					newRoute[data.PageKey] = i + diff1;
					ul.Add(CreatePagerLinkElement(i.ToString(), urlHelper.Action(actionText, controllerText, newRoute)));
				}
			}

			//if (currentPage + sidePages + 1 == pages - 2)
			//{
			//    newRoute[data.PageKey] = pages - 2;
			//    ul.Add(CreatePagerLinkElement((pages - 1).ToString(), urlHelper.Action(actionText, controllerText, newRoute)));
			//}

			// right side10 pages
			int right10 = currentPage1 + sidePages + 1;
			for (; right10 < lastPage1; right10++)
			{
				if (right10 % 10 == 0)
					break;
			}

			for (int i=0; i < sidePages10; i++)
			{
				int page = right10 + 10 * i;
				if (page > lastPage1 - 1)
					break;
				newRoute[data.PageKey] = page + diff1;
				ul.Add(CreatePagerLinkElement(page.ToString(), urlHelper.Action(actionText, controllerText, newRoute)));
			}
			
			// end
			if (currentPage == lastPage)
			{
				ul.Add(CreatePagerLinkElement(lastPage1.ToString(), null, true));
			}
			else
			{
				newRoute[data.PageKey] = lastPage;
				ul.Add(CreatePagerLinkElement(lastPage1.ToString(), urlHelper.Action(actionText, controllerText, newRoute)));
			}

			return MvcHtmlString.Create(container.ToString());
		}

		/// <summary>
		/// Creates "li" element for pager items.
		/// </summary>
		/// <param name="text">Item text.</param>
		/// <param name="url">Item url.</param>
		/// <param name="selected">Is item selected.</param>
		/// <returns>XElement.</returns>
		private static XElement CreatePagerLinkElement(string text, string url = null, bool selected = false)
		{
			if (String.IsNullOrEmpty(text))
				throw new ArgumentNullException("text");

			XElement e = new XElement("li");
			if (selected)
				e.Add(new XAttribute("class", "selected"));

			if (String.IsNullOrEmpty(url))
				e.Add(text);
			else
				e.Add(new XElement("a", new XAttribute("href", url), text));

			return e;
		}

		#endregion

		#region ImageUpload

		/// <summary>
		/// Renders "ImageUpload" partial view.
		/// If model is LinqToSql class and File is null then File is taken from model using data.FieldName.
		/// </summary>
		/// <param name="helper">Html helper.</param>
		/// <param name="fieldName">File upload field name.</param>
		/// <param name="radioName">Radio selection field name.</param>
		/// <param name="htmlAttributes">Html attributes.</param>
		public static MvcHtmlString ImageUpload(this HtmlHelper helper, string fieldName, string radioName, bool disableDelete = false)
		{
			// check params
			if (String.IsNullOrEmpty(fieldName))
				throw new ArgumentNullException("fieldName");
			if (String.IsNullOrEmpty(radioName))
				throw new ArgumentNullException("radioName");

			// get file url
			FileUploadModel data = new FileUploadModel(fieldName, radioName);
			data.DisableDelete = disableDelete;

			IdEntity model = helper.ViewData.Model as IdEntity;
			if (model != null)
			{
				UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
				data.FileUrl = url.ImageEntity(model, fieldName, 150);
			}
			return helper.Partial("ImageUpload", data);
		}

		#endregion

		#region Captcha

		/// <summary>
		/// Returns img element with url to captcha and hidden field with key value.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="htmlAttributes">Img html attributes.</param>
		public static MvcHtmlString Captcha(this HtmlHelper helper, IDictionary<string, object> htmlAttributes)
		{
			// get captcha key
			UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
			string key = helper.ViewData[CaptchaManager.ViewDataKey] as string;
			if (String.IsNullOrEmpty(key))
				throw new InvalidOperationException("Can not create captcha img tag: ViewData does not contains \"" + CaptchaManager.ViewDataKey + "\" item.");

			// create img tag
			TagBuilder img = new TagBuilder("img");
			img.MergeAttributes(htmlAttributes);
			img.MergeAttribute("src", url.Captcha(key), true);

			// create hidden
			StringBuilder sb = new StringBuilder();
			sb.Append(img.ToString(TagRenderMode.SelfClosing));
			sb.Append(helper.Hidden(CaptchaManager.HtmlKeyName, key));
			return MvcHtmlString.Create(sb.ToString());
		}

		/// <summary>
		/// Returns img element with url to captcha and hidden field with key value.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="htmlAttributes">Img html attributes.</param>
		public static MvcHtmlString Captcha(this HtmlHelper helper, object htmlAttributes)
		{
			return Captcha(helper, new RouteValueDictionary(htmlAttributes));
		}

		/// <summary>
		/// Returns img element with url to captcha and hidden field with key value.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		public static MvcHtmlString Captcha(this HtmlHelper helper)
		{
			return Captcha(helper, null);
		}

		/// <summary>
		/// Returns a text input element to enter captcha text.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		public static MvcHtmlString CaptchaTextBox(this HtmlHelper helper)
		{
			return helper.TextBox(CaptchaManager.HtmlTextName);
		}

		/// <summary>
		/// Returns a text input element to enter captcha text.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="htmlAttributes">Input tag html attributes.</param>
		public static MvcHtmlString CaptchaTextBox(this HtmlHelper helper, object htmlAttributes)
		{
			return helper.TextBox(CaptchaManager.HtmlTextName, null, htmlAttributes);
		}

		#endregion

		#region Buttons

		///// <summary>
		///// Generates link button with icon.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="help">Help for the link button.</param> 
		///// <param name="helpTitle">Help title for the link button.</param>
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//public static void LinkButton(this HtmlHelper helper, string text, string iconName, string url,
		//                                    object additionalHtmlAttributes = null,
		//                                    string help = null, string helpTitle = null)
		//{
		//    LinkButton(helper, text, iconName, url, new RouteValueDictionary(additionalHtmlAttributes), help, helpTitle);
		//}

		///// <summary>
		///// Generates link button with icon.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="help">Help for the link button.</param> 
		///// <param name="helpTitle">Help title for the link button.</param>
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//public static void LinkButton(this HtmlHelper helper, string text, string iconName, string url,
		//                                IDictionary<string, object> additionalHtmlAttributes,
		//                                string help = null, string helpTitle = null)
		//{
		//    helper.RenderPartial("LinkButtonTemplate", new LinkButtonData
		//    {
		//        Text = text,
		//        IconName = iconName,
		//        Url = url,
		//        ContextHelpText = help,
		//        ContextHelpTitle = helpTitle,
		//        AdditionalHtmlAttributes = additionalHtmlAttributes
		//    });
		//}

		///// <summary>
		///// Generates link button opening tag.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="helpTitle">Help title for the link button.</param>
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//internal static void LinkButtonBeginInner(this HtmlHelper helper, string text, string iconName, string url,
		//                                        IDictionary<string, object> additionalHtmlAttributes, string helpTitle = null)
		//{
		//    helper.RenderPartial("LinkButtonBeginTemplate", new LinkButtonData
		//    {
		//        Text = text,
		//        IconName = iconName,
		//        Url = url,
		//        ContextHelpTitle = helpTitle,
		//        AdditionalHtmlAttributes = additionalHtmlAttributes
		//    });
		//}

		///// <summary>
		///// Generates link button opening tag.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="helpTitle">Help title for the link button.</param> 
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//internal static void LinkButtonBeginInner(this HtmlHelper helper, string text, string iconName, string url,
		//                                        object additionalHtmlAttributes = null, string helpTitle = null)
		//{
		//    LinkButtonBeginInner(helper, text, iconName, url, new RouteValueDictionary(additionalHtmlAttributes), helpTitle);
		//}

		///// <summary>
		///// Generates link button closing tag.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		//internal static void LinkButtonEndInner(this HtmlHelper helper)
		//{
		//    helper.RenderPartial("LinkButtonEndTemplate", new LinkButtonData());
		//}

		///// <summary>
		///// Generates link button opening tag.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="helpTitle">Help title for the link button.</param>
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//public static MvcLinkButton LinkButtonBegin(this HtmlHelper helper, string text, string iconName, string url,
		//                                        IDictionary<string, object> additionalHtmlAttributes, string helpTitle = null)
		//{
		//    return new MvcLinkButton(helper, text, iconName, url, additionalHtmlAttributes, helpTitle);
		//}

		///// <summary>
		///// Generates link button opening tag.
		///// </summary>
		///// <param name="helper">Html helper.</param>
		///// <param name="text">Text on the button.</param>
		///// <param name="iconName">Icon name (png should be in the /Content/Icons).</param>
		///// <param name="url">Url for the link.</param>
		///// <param name="helpTitle">Help title for the link button.</param> 
		///// <param name="additionalHtmlAttributes">Attributes for link ('a' html tag). "_" is replaced with "-"!</param>
		//public static MvcLinkButton LinkButtonBegin(this HtmlHelper helper, string text, string iconName, string url,
		//                                        object additionalHtmlAttributes = null, string helpTitle = null)
		//{
		//    return LinkButtonBegin(helper, text, iconName, url, new RouteValueDictionary(additionalHtmlAttributes), helpTitle);
		//}

		#endregion

		#region Hidden

		/// <summary>
		/// Renders hidden field for each key and value from http query string.
		/// </summary>
		/// <param name="helper">HtmlHelper.</param>
		/// <param name="excludeKeys">Keys to exclude from hidden fields.</param>
		/// <returns>Html hidden tags.</returns>
		public static MvcHtmlString HiddenQueryString(this HtmlHelper helper, params string[] excludeKeys)
		{
			StringBuilder sb = new StringBuilder();
			NameValueCollection q = helper.ViewContext.HttpContext.Request.QueryString;

			for (int i = 0; i < q.Count; i++)
			{
				string key = q.GetKey(i);
				if (!excludeKeys.Any(k => k.ToLower() == key.ToLower()))
				{
					sb.Append(helper.Hidden(key, q[i]));
					sb.Append("\r\n");
				}
			}
			return MvcHtmlString.Create(sb.ToString());
		}

		#endregion

		#region Partial

		/// <summary>
		/// Renders the specified partial view as an HTML-encoded string.
		/// </summary>
		/// <param name="html">The HTML helper instance that this method extends.</param>
		/// <param name="partialViewName">The name of the partial view.</param>
		/// <param name="model">The model for the partial view.</param>
		/// <param name="htmlFieldPrefix">HTML field prefix.</param>
		/// <returns>The partial view that is rendered as an HTML-encoded string.</returns>
		public static MvcHtmlString Partial(this HtmlHelper html, string partialViewName, object model, string htmlFieldPrefix)
		{
			var viewData = new ViewDataDictionary(html.ViewData)
			{
				TemplateInfo = new TemplateInfo
				{
					HtmlFieldPrefix = htmlFieldPrefix
				}
			};
			return html.Partial(partialViewName, model, viewData);
		}

		#endregion
	}
}
